﻿namespace itau.im.agendador
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using itau.im.negocio;
    using itau.im.entidade;
    using EnterpriseLibraryWrapper;
    using System.Threading;
    using LogOnline;

    public class Agendador
    {

        #region Constantes

        public const string MASCARA_DATA = "dd/MM/yyyy HH:mm:ss";
        public const string DOMINIO_AGENDADOR = "IMM6501";
        public const string PARAMETRO_ULTIMAEXECUCAO = "DTHRULTIMAEXECUCAO";
        public const string PARAMETRO_JANELA = "JANELASEGUNDOS";
        public const int EXECUCAO_AGENDADA = 11;

        #endregion

        #region Atributos

        Int32 janela;
        DateTime dataHoraUltimaExecucao;
        DateTime dataHoraExecucaoAtual;
        DateTime hoje = new DateTime();
        bool ehFeriado;

        ICurvaBS curva;
        IConfiguracoesBS configuracoes;
        IExecucaoCurvaBS execucaoCurva;

        static wILog logOn = new wILogClass();

        private static string _nomeAplic = "IMM6501";
        private static Timer _timer = null;

        #endregion

        #region Construtor

        public Agendador()
        {
            curva = new CurvaBS();
            configuracoes = new ConfiguracoesBS();
            execucaoCurva = new ExecucaoCurvaBS();
            logOn.IniciarLog(ref _nomeAplic);
        }

        #endregion

        #region Métodos Públicos

        public void Agendar()
        {
            try
            {
                Console.WriteLine("Agendador ...");
                AgendarCurvas();
            }
            catch (System.ServiceModel.FaultException ex)
            {
                EnterpriseLibraryFactory.GetExceptionHandler().HandleException(ex, "ErrosAgendador");
            }
            catch (Exception ex)
            {
                EnterpriseLibraryFactory.GetExceptionHandler().HandleException(ex, "ErrosAgendador");
            }
            finally
            {
                LimparObjetos();
            }
        }

        #endregion

        #region Métodos Privados

        private void AgendarCurvas()
        {
            GravarLog("Inicio");

            CriarTimerPatrol();

            ObterJanelaDeExecucaoEmSegundos();

            GravarLog(string.Format("Janela de execução: {0}", janela));

            while (true)
            {
                try
                {
                    ObterDataHoraUltimaExecucao();

                    GravarLog(string.Format("Data e hora última execução: {0}", dataHoraUltimaExecucao.ToString(MASCARA_DATA)));

                    ProcessarAgendamentoDasCurvasAtivas();

                    AtualizarDataHoraUltimaExecucao();

                    AguardarProximaExecucao();
                }
                catch (System.ServiceModel.FaultException ex)
                {
                    EnterpriseLibraryFactory.GetExceptionHandler().HandleException(ex, "ErrosAgendador");
                }
                catch (Exception ex)
                {
                    EnterpriseLibraryFactory.GetExceptionHandler().HandleException(ex, "ErrosAgendador");
                }
            }
        }

        private void CriarTimerPatrol()
        {
            _timer = new Timer(new TimerCallback(GerarLogPatrol), null, 0, 3 * 60000);
        }

        private void ObterJanelaDeExecucaoEmSegundos()
        {
            string janelaConfiguracao = configuracoes.ObterConfiguracaoPorDominioEChave(DOMINIO_AGENDADOR, PARAMETRO_JANELA);

            try
            {
                janela = ConverterParaInteiro(janelaConfiguracao);
            }
            catch (Exception)
            {
                throw new Exception("O parâmetro Janela de Execução não foi cadastrado, ou está em um formato inválido.");
            }
        }

        private void ObterDataHoraUltimaExecucao()
        {
            string strDataHoraUltimaExecucao = configuracoes.ObterConfiguracaoPorDominioEChave(DOMINIO_AGENDADOR, PARAMETRO_ULTIMAEXECUCAO);

            try
            {
                dataHoraUltimaExecucao = DateTime.ParseExact(strDataHoraUltimaExecucao, MASCARA_DATA, null);
            }
            catch (Exception)
            {
                throw new Exception(string.Format("A data e hora última execução da configuração não está no formato {0} esperado, ou a chave do parâmetro não existe. Nome da chave: {1} e nome do dominio: {2}", MASCARA_DATA, PARAMETRO_ULTIMAEXECUCAO, DOMINIO_AGENDADOR));
            }
        }

        private void ProcessarAgendamentoDasCurvasAtivas()
        {
            if (NaoEhFinalDeSemana()
                && NaoEhFeriado())
            {
                dataHoraExecucaoAtual = System.DateTime.Now;

                if (UltimaExecucaoFoiOntem())
                {
                    CriarAgendamentoAnteriorAMeiaNoite();
                }

                CriarAgendamentoDasCurvasAtivas();
            }
            else
            {
                AguardarExecucaoPorUmaHora();
            }
        }

        private bool NaoEhFeriado()
        {
            if (hoje.Date != System.DateTime.Now.Date)
            {
                CalendarioBS calendario = new CalendarioBS();
                hoje = System.DateTime.Now.Date;
                ehFeriado = calendario.EhFeriadoNacional(hoje);
            }

            return !ehFeriado;
        }

        private void AguardarExecucaoPorUmaHora()
        {
            System.Threading.Thread.Sleep(60 * 60 * 60000);
        }

        private bool UltimaExecucaoFoiOntem()
        {
            return dataHoraUltimaExecucao.Date != dataHoraExecucaoAtual.Date;
        }

        private void CriarAgendamentoAnteriorAMeiaNoite()
        {
            execucaoCurva.CriarAgendamentoDasCurvasAtivas(dataHoraUltimaExecucao.Date.Add(new TimeSpan(0, 23, 59, 59, 0)), 11, 0, DateTime.MinValue);
        }

        private void CriarAgendamentoDasCurvasAtivas()
        {
            execucaoCurva.CriarAgendamentoDasCurvasAtivas(dataHoraExecucaoAtual, 11, 0, DateTime.MinValue);
        }

        private void AtualizarDataHoraUltimaExecucao()
        {
            configuracoes.AtualizarConfiguracao(DOMINIO_AGENDADOR, PARAMETRO_ULTIMAEXECUCAO, dataHoraExecucaoAtual.ToString(MASCARA_DATA));
        }

        private void AguardarProximaExecucao()
        {
            if (OTempoDeExecucaoNaoUltrapassouAJanela())
            {
                GravarLog(string.Format("Fim da execução: {0}", System.DateTime.Now.ToString(MASCARA_DATA)));

                System.Threading.Thread.Sleep(Convert.ToInt32((dataHoraExecucaoAtual.AddSeconds(janela) - System.DateTime.Now).TotalMilliseconds));
            }
        }

        private bool NaoEhFinalDeSemana()
        {
            return (System.DateTime.Now.DayOfWeek != DayOfWeek.Saturday && System.DateTime.Now.DayOfWeek != DayOfWeek.Sunday);
        }

        private bool OTempoDeExecucaoNaoUltrapassouAJanela()
        {
            return dataHoraExecucaoAtual.AddSeconds(janela) > System.DateTime.Now;
        }

        private void GravarLog(string mensagem)
        {
            EnterpriseLibraryFactory.GetLogger().Write(1, "Agendador", "Log", mensagem);
        }

        private int ConverterParaInteiro(string variavel)
        {
            return Convert.ToInt32(variavel);
        }

        private void LimparObjetos()
        {
            curva = null;
            configuracoes = null;
            execucaoCurva = null;
            GravarLog("Fim");
        }

        static void GerarLogPatrol(Object state)
        {
            logOn.TxtPatrol(ref _nomeAplic);
        }

        #endregion

    }
}
